﻿using System;
using System.Threading;
using System.Windows.Threading;
using Iorz.DomainSearch.Utils.Provider;

namespace Iorz.DomainSearch.Utils.Worker
{
    public class SearchWorker : BaseWorker
    {
        public const string ZM_LEFT_RIGHT = "abcdefghijklmnopqrstuvwxyz0123456789";
        public const string ZM_MIDDLE = "abcdefghijklmnopqrstuvwxyz0123456789-";

        public Action<bool, string, string> OnNotifyUI { private get; set; }

        private IProvider mProvider = null;
        private string[] mPrefixs = null;
        private string[] mSuffixs = null;
        /// <summary>
        /// 源码格式（替换符{domain}）
        /// </summary>
        private string mFormat = null;
        private int mDomainLength = 0;
        private int mTryCount = 0;
        private int mCount = 0;
        private Random mRandom = new Random();

        public SearchWorker(Dispatcher owner, IProvider provider, string[] prefixs, string[] suffixs, string format, int domainLength)
            : base(owner)
        {
            mProvider = provider;
            mPrefixs = prefixs;
            mSuffixs = suffixs;
            mFormat = format.ToLower();
            mDomainLength = domainLength;
        }

        /// <summary>
        /// 抛弃工作线程
        /// </summary>
        public override void Discard()
        {
            lock (mLock)
            {
                OnNotifyUI = null;
                mProvider = null;
                base.Discard();
            }
        }

        /// <summary>
        /// 执行函数
        /// </summary>
        protected override void Run()
        {
            if ((mSuffixs == null || mSuffixs.Length == 0) || (mPrefixs == null || mPrefixs.Length == 0))
            {
                Asynchronous(OnNotifyUI, false, null, "未指定域名前缀和后缀，线程已终止");
                return;
            }

            mTryCount = 0;
            mCount = 0;

            if (IsTerminated())
                return;

            foreach (var item in mPrefixs)
            {
                if (IsTerminated())
                    return;

                var length = mDomainLength - mPrefixs[0].Length;
                if (length < 0)
                {
                    Asynchronous(OnNotifyUI, false, null, "指定域名长度小于域名前缀长度，线程已终止");
                    return;
                }

                if (!GetDomain(item, length, 0, OnDomainSearched))
                    return;
            }
        }

        private string GetFormatDomain(string domain)
        {
            return mFormat.Replace("{domain}", domain);
        }

        private bool OnDomainSearched(string domain)
        {
            foreach (var suffix in mSuffixs)
            {
                if (IsTerminated())
                    return false;

                var formatDoamin = GetFormatDomain(domain + suffix);
                var isAvail = false;
                if (mProvider != null && !mProvider.Search(formatDoamin, out isAvail))
                {
                    Asynchronous(OnNotifyUI, false, formatDoamin, $"{mProvider.GetName()}接口返回异常，搜索失败");
                    mTryCount++;
                }

                Asynchronous(OnNotifyUI, true, formatDoamin, "域名" + (isAvail ? "可注册" : "不可用"));
                mCount++;

                if (IsTerminated())
                    return false;

                if (mTryCount > 2)
                {
                    if (!Sleep(mRandom.Next(40, 80))) return false;
                    mCount = 0;
                    mTryCount = 0;
                }
                else if (mTryCount > 0)
                {
                    if (!Sleep(mRandom.Next(5, 10))) return false;
                    mCount = 0;
                }
                else if (mCount > 2)
                {
                    if (!Sleep(mRandom.Next(3, 6))) return false;
                    mCount = 0;
                    mTryCount = 0;
                }
                else
                {
                    if (!Sleep(mRandom.Next(1, 3))) return false;
                }
            }

            return true;
        }

        private bool GetDomain(string domain, int length, int currentLength, Func<string, bool> handler)
        {
            if (currentLength >= length)
                return handler.Invoke(domain);

            var str = ZM_MIDDLE;
            if (currentLength == 0 || currentLength == length - 1)
                str = ZM_LEFT_RIGHT;

            currentLength++;
            foreach (var item in str.ToCharArray())
            {
                if (IsTerminated())
                    return false;

                if (!GetDomain(domain + item, length, currentLength, handler))
                    return false;
            }

            if (IsTerminated())
                return false;

            return true;
        }

        private bool Sleep(int seconds)
        {
            while (seconds > 0)
            {
                if (IsTerminated())
                    return false;

                seconds--;
                Thread.Sleep(1000);
            }

            return true;
        }
    }
}
